package cn.xing.async;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;

import cn.xing.util.JedisAdapter;
import cn.xing.util.RedisKeyUtil;

/**
 * 事件处理，消费者
 * @author Xing
 *
 */
/**
 * 
 * 实现InitializingBean接口：EventConsumer对象属性初始化之后会自动执行afterPropertiesSet方法，一般用于数据的初始化
 * 实现ApplicationContextAware接口，可以让bean（EventConsumer）获取到所在的spring容器，进而可以获取到所有的bean
 */
@Service
public class EventConsumer implements InitializingBean, ApplicationContextAware {
	private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
	// 一个事件可能对应多个处理器
	private Map<EventType, List<EventHandler>> config = new HashMap<EventType, List<EventHandler>>();
	private ApplicationContext applicationContext;

	@Autowired
	JedisAdapter jedisAdapter;

	/**
	 * 获取所有的处理器对象，将处理器支持的类型为key，处理器为List<value>,存入map。
	 * 结果是：Map<点赞，List<通知用户处理器、活跃度处理器。。。>>、Map<评论，List<通知用户处理器、活跃度处理器。。。>>
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		// 找出所有实现了EventHandler接口的事件处理器bean
		Map<String, EventHandler> beans = applicationContext.getBeansOfType(EventHandler.class);
		if (beans != null) {
			//遍历所有处理器
			for (Map.Entry<String, EventHandler> entry : beans.entrySet()) {
				// 获得当前处理器支持的事件列表
				List<EventType> eventTypes = entry.getValue().getSupportEventTypes();
				// 一个事件类型可能对应多个处理器
				for (EventType type : eventTypes) {
					if (!config.containsKey(type)) {
						// 存入事件类型key
						config.put(type, new ArrayList<EventHandler>());
					}
					// 存储事件处理器List<value>，处理器可处理多个类型的事件
					config.get(type).add(entry.getValue());
				}
			}
		}

		// 开启一个死循环线程，不断从redis队列中取事件
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					String key = RedisKeyUtil.getEventQueueKey();
					List<String> events = jedisAdapter.brpop(0, key);// 从右边取出事件，阻塞式
					System.out.println("事件消费者拿到事件了================================");
					for (String message : events) {
						// 剔除key
						if (message.equals(key)) {
							continue;
						}

						EventModel eventModel = JSON.parseObject(message, EventModel.class);
						if (!config.containsKey(eventModel.getType())) {
							logger.error("不能识别的事件");
							continue;
						}
						// 从map中取出对这个事件感兴趣的处理器列表，进行遍历，让每个处理器进行处理
						for (EventHandler handler : config.get(eventModel.getType())) {
							handler.doHandle(eventModel);
						}
					}
				}
			}
		});
		thread.start();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}
